<!DOCTYPE html>
<html lang="zh-cn" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>并发编程 | 爱做梦的锤子</title>
    <meta name="description" content="爱做梦的锤子">
    <meta name="generator" content="VitePress v1.1.3">
    <link rel="preload stylesheet" href="/assets/style.CamsuKc9.css" as="style">
    
    <script type="module" src="/assets/app.7I_MwdqG.js"></script>
    <link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
    <link rel="modulepreload" href="/assets/chunks/framework.Doyu4iOK.js">
    <link rel="modulepreload" href="/assets/chunks/theme.uNz9H00L.js">
    <link rel="modulepreload" href="/assets/test.md.DXBSkH1F.lean.js">
    <script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
    <script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
    <script>import("/pagefind/pagefind.js").then(i=>{window.__pagefind__=i,i.init()}).catch(()=>{});</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-ad552957><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0f60ec36></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0f60ec36> Skip to content </a><!--]--><!----><header class="VPNav" data-v-ad552957 data-v-ae24b3ad><div class="VPNavBar top" data-v-ae24b3ad data-v-ccf7ddec><div class="wrapper" data-v-ccf7ddec><div class="container" data-v-ccf7ddec><div class="title" data-v-ccf7ddec><div class="VPNavBarTitle" data-v-ccf7ddec data-v-ab179fa1><a class="title" href="/" data-v-ab179fa1><!--[--><!--]--><!--[--><img class="VPImage logo" src="/logo.png" alt="te-amo" data-v-8426fc1a><!--]--><span data-v-ab179fa1>爱做梦的锤子</span><!--[--><!--]--></a></div></div><div class="content" data-v-ccf7ddec><div class="content-body" data-v-ccf7ddec><!--[--><!--]--><div class="blog-search search" data-pagefind-ignore="all" data-v-ccf7ddec style="--1af60572:1;" data-v-a3e043fe><div class="nav-search-btn-wait" data-v-a3e043fe><span data-v-a3e043fe><svg width="14" height="14" viewBox="0 0 20 20" data-v-a3e043fe><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round" data-v-a3e043fe></path></svg></span><span style="" class="search-tip" data-v-a3e043fe>搜索</span><span style="" class="metaKey" data-v-a3e043fe> K </span></div><!----></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-ccf7ddec data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="/" tabindex="0" data-v-7f418b0f data-v-9c663999><!--[--><span data-v-9c663999>Home</span><!--]--></a><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup active" data-v-7f418b0f data-v-b6c34ac9><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-b6c34ac9><span class="text" data-v-b6c34ac9><!----><span data-v-b6c34ac9>计算机基础</span><span class="vpi-chevron-down text-icon" data-v-b6c34ac9></span></span></button><div class="menu" data-v-b6c34ac9><div class="VPMenu" data-v-b6c34ac9 data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuLink" data-v-e7ea1737 data-v-43f1e123><a class="VPLink link active" href="/test.html" data-v-43f1e123><!--[-->数据结构与算法<!--]--></a></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-ccf7ddec data-v-e6aabb21><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="Switch to dark theme" aria-checked="false" data-v-e6aabb21 data-v-d1f28634 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-d1f28634></span><span class="vpi-moon moon" data-v-d1f28634></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-ccf7ddec data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/vuejs/vitepress" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-eee4e7cb><span class="vpi-social-github" /></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-ccf7ddec data-v-d0bd9dde data-v-b6c34ac9><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-b6c34ac9><span class="vpi-more-horizontal icon" data-v-b6c34ac9></span></button><div class="menu" data-v-b6c34ac9><div class="VPMenu" data-v-b6c34ac9 data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-d0bd9dde><div class="item appearance" data-v-d0bd9dde><p class="label" data-v-d0bd9dde>Appearance</p><div class="appearance-action" data-v-d0bd9dde><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="Switch to dark theme" aria-checked="false" data-v-d0bd9dde data-v-d1f28634 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-d1f28634></span><span class="vpi-moon moon" data-v-d1f28634></span><!--]--></span></span></button></div></div></div><div class="group" data-v-d0bd9dde><div class="item social-links" data-v-d0bd9dde><div class="VPSocialLinks social-links-list" data-v-d0bd9dde data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/vuejs/vitepress" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-eee4e7cb><span class="vpi-social-github" /></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-ccf7ddec data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><div class="divider" data-v-ccf7ddec><div class="divider-line" data-v-ccf7ddec></div></div></div><!----></header><div class="VPLocalNav empty fixed" data-v-ad552957 data-v-a6f0e41e><div class="container" data-v-a6f0e41e><!----><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-a6f0e41e data-v-267dd0ed><button data-v-267dd0ed>Return to top</button><!----></div></div></div><!----><div class="VPContent" id="VPContent" data-pagefind-body data-v-ad552957 data-v-1428d186><div class="VPDoc has-aside" data-v-1428d186 data-v-39a288b8><!--[--><!--]--><div class="container" data-v-39a288b8><div class="aside" data-v-39a288b8><div class="aside-curtain" data-v-39a288b8></div><div class="aside-container" data-v-39a288b8><div class="aside-content" data-v-39a288b8><div class="VPDocAside" data-v-39a288b8 data-v-3f215769><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" role="navigation" data-v-3f215769 data-v-935f8a84><div class="content" data-v-935f8a84><div class="outline-marker" data-v-935f8a84></div><div class="outline-title" role="heading" aria-level="2" data-v-935f8a84>On this page</div><nav aria-labelledby="doc-outline-aria-label" data-v-935f8a84><span class="visually-hidden" id="doc-outline-aria-label" data-v-935f8a84> Table of Contents for current page </span><ul class="VPDocOutlineItem root" data-v-935f8a84 data-v-b933a997><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-39a288b8><div class="content-container" data-v-39a288b8><!--[--><!--]--><main class="main" data-v-39a288b8><div style="position:relative;" class="vp-doc _test" data-v-39a288b8><div><h1 id="并发编程" tabindex="-1">并发编程 <a class="header-anchor" href="#并发编程" aria-label="Permalink to &quot;并发编程&quot;">​</a></h1><h2 id="_1-线程的创建" tabindex="-1">1.线程的创建 <a class="header-anchor" href="#_1-线程的创建" aria-label="Permalink to &quot;1.线程的创建&quot;">​</a></h2><h3 id="什么是线程" tabindex="-1">什么是线程 <a class="header-anchor" href="#什么是线程" aria-label="Permalink to &quot;什么是线程&quot;">​</a></h3><p>线程是操作系统可以调度的最小单元，每个线程拥有自己的计数器、堆栈和局部变量等属性，能够访问共享的内存变量。</p><h3 id="什么是进程" tabindex="-1">什么是进程 <a class="header-anchor" href="#什么是进程" aria-label="Permalink to &quot;什么是进程&quot;">​</a></h3><p>运行一个程序时，就会创建一个进程。</p><h3 id="怎么创建一个线程" tabindex="-1">怎么创建一个线程 <a class="header-anchor" href="#怎么创建一个线程" aria-label="Permalink to &quot;怎么创建一个线程&quot;">​</a></h3><ul><li>new Thread，并传入参数，初始化对象</li><li>调用start方法启动线程</li></ul><h3 id="线程属性" tabindex="-1">线程属性 <a class="header-anchor" href="#线程属性" aria-label="Permalink to &quot;线程属性&quot;">​</a></h3><ul><li>线程名：最好设置，方便使用jstack分析排查问题时给予提示</li><li>优先级属性：决定线程需要被分配多少处理器资源。1-10，默认5，数字越大优先级越高。不能作为程序正确性以来，操作系统可能不会理会这个优先级设定</li><li>daemon属性：表示线程是一种支持线程也叫守护线程。默认false，当虚拟机没有非daemon线程时，虚拟机就会退出。特点1:在线程启动前设置；特点2：finally代码块不一定被执行</li><li>interrupted属性：线程标识位属性，表示当前线程是否被其他线程进行了中断操作。线程自身可以使用isInterrupted()方法来判断自己是否被中断。线程在终止状态时，该标识为false</li></ul><h3 id="等待-通知机制-object中关于线程的方法" tabindex="-1">等待/通知机制(Object中关于线程的方法) <a class="header-anchor" href="#等待-通知机制-object中关于线程的方法" aria-label="Permalink to &quot;等待/通知机制(Object中关于线程的方法)&quot;">​</a></h3><p>wait:调用该方法的线程进入等待状态，只有等到另外的线程通知或者中断才会返回，调用该方法会释放对象的锁</p><p>notify:通知一个在对象上等待的线程，使其从wait方法返回，而返回的前提是该线程获取到了对象锁</p><p>notifyAll:通知所有等待在该对象上的线程</p><h3 id="thread-join-操作" tabindex="-1">thread.join()操作 <a class="header-anchor" href="#thread-join-操作" aria-label="Permalink to &quot;thread.join()操作&quot;">​</a></h3><p>当前线程调用了AThread的join方法其含义是当前线程要等待AThread终止后才继续执行，即当前线程进入等待状态。</p><h3 id="_2-线程状态及其转换" tabindex="-1">2.线程状态及其转换 <a class="header-anchor" href="#_2-线程状态及其转换" aria-label="Permalink to &quot;2.线程状态及其转换&quot;">​</a></h3><ul><li><p>状态类型</p><ul><li>NEW - 初始</li><li>RUNNABLE - 可运行</li><li>RUNNING 运行中</li><li>READY - 就绪</li><li>WAITING - 等待</li><li>TIME_WAITING - 超时等待</li><li>BLOCK - 阻塞</li><li>TERMINATED - 终止</li></ul></li><li><p>转换路径</p><p>实例化Thread对象进入NEW状态</p><p>调用Thread.start()，由NEW状态进入RUNNABLE状态</p><p>线程获取到CPU时间片，由RUNNABLE状态进入RUNNING状态</p><p>调用Thread.yield(),由RUNNING状态进入READY状态</p><p>获取到CPU时间片，由READY状态进入RUNNING状态</p><p>调用Object.wait()或者Thread.join()或者LockSupport.park()，由RUNNING状态进入WAITING状态</p><p>其他线程调用Object.notify()或者Object.notifyAll()或者LockSupport.unPark()或者其他线程进入终止状态，由WAITING状态进入READY状态</p><p>调用Thread.sleep(long)或者Object.wait(long)或者Thread.join(long)或者LockSupport.parkNanos()或者LockSupport.parkUntil()时，由RUNNING状态进入TIME_WAITING状态</p><p>其他线程调用Object.notify()或者Object.notifyAll()或者LockSupport.unPark()或者其他线程进入终止状态或者超时时间到了，由TIME_WAITING状态进入READY状态</p><p>当线程等待获取锁时，由RUNNING状态进入BLOCKED状态</p><p>当线程获取到锁时，由BLOCKED状态进入READY状态</p><p>当线程终止，直接进入TERMINATED状态</p></li><li><p>线程上下文切换的定义</p><p>CPU通过时间片分配算法来循环执行任务，当一个任务的时间片执行完后进入下一个任务。而在进入下一个任务之前它会保存当前任务的状态，以便下次切换回该任务时，可以再加载这个任务状态。任务从保存到再加载的过程称为一次上下文切换。</p></li><li><p>如何减少上下文切换</p><p>无锁并发编程</p><p>CAS算法</p><p>使用最少的线程，避免不必要的线程切换</p></li></ul><h2 id="_3-线程池" tabindex="-1">3.线程池 <a class="header-anchor" href="#_3-线程池" aria-label="Permalink to &quot;3.线程池&quot;">​</a></h2><h3 id="为什么要使用线程池" tabindex="-1">为什么要使用线程池 <a class="header-anchor" href="#为什么要使用线程池" aria-label="Permalink to &quot;为什么要使用线程池&quot;">​</a></h3><ul><li><strong>降低资源消耗</strong>。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。</li><li><strong>提高响应速度</strong>。当任务到达时，任务可以不需要等到线程创建就能立即执行。</li><li><strong>提高线程的可管理性</strong>。线程是稀缺资源，如果无限制的创建，不仅会消耗系统资源，还会降低系统的稳定性，使用线程池可以进行统一的分配，调优和监控。</li></ul><h3 id="线程池实现原理-执行流程" tabindex="-1">线程池实现原理(执行流程) <a class="header-anchor" href="#线程池实现原理-执行流程" aria-label="Permalink to &quot;线程池实现原理(执行流程)&quot;">​</a></h3><ul><li>先看核心线程池是否满了，如果不是就创建一个新的线程，如果是则要下一个流程</li><li>核心线程池满了就需要判断任务队列是否满了，如果不是加入队列，如果是则要进入下一个流程</li><li>队列满了就需要判断线程池是否满了(是否达到最大线程数)，如果不是则创建新线程，如果是则执行拒绝策略</li></ul><p>线程池参数</p><ul><li>corePoolSize：线程池核心线程数，当线程池的线程数量小于核心线程数时，提交一个任务就会创建新的线程</li><li>runnableTaskQueue：任务队列，用于保存等待执行的任务的阻塞队列，常用ArrayBlockingQueue，LinkedBlockingQueue，SynchronousQueue，PriorityBlockingQueue</li><li>maximumPoolSize：线程池最大线程数，如果任务队列满了，并且已经创建的线程数小于最大线程数，则线程池会创建新的线程来执行任务，如果采用无界队列作为任务队列，这个参数就没有意义了</li><li>ThreadFactory：用于设置创建线程的工厂</li><li>RejectedExecutionHandler：拒绝策略，当任务队列和线程池都满了，对新提交的任务就会执行拒绝策略 <ul><li>拒绝策略默认情况下是AbortPolicy，直接抛出异常；</li><li>CallerRunsPolicy，只用调用者所在线程来执行任务；</li><li>DiscardOldestPolicy，丢弃队列里最老的任务，并执行当前任务；</li><li>DiscardPolicy，直接丢弃不处理</li></ul></li><li>keepAliveTime：线程存活时间，当线程池的线程数量大于核心线程数，没有新的任务提交，核心线程池外的线程不会立即销毁，而是会等待一段时间，等待时间超过keepAliveTime时就会销毁掉。</li><li>unit：keepAliveTime时间的单位</li></ul><p>线程池创建</p><ul><li>Executors <ul><li>newFixedThreadPool：核心线程数和最大线程数一致</li><li>newCachedThreadPool：核心线程数为0，最大线程数为设置的线程数</li><li>newScheduledThreadPool：可调度线程池</li><li>newSingleThreadExecutor：单线程，核心线程数和最大线程数都为1</li></ul></li></ul><p>线程池状态</p><ul><li><code>RUNNING</code>：运行状态，接受新的任务并且处理队列中的任务。</li><li><code>SHUTDOWN</code>：关闭状态(调用了 shutdown 方法)。不接受新任务，,但是要处理队列 中的任务。</li><li><code>STOP</code>：停止状态(调用了 shutdownNow 方法)。不接受新任务，也不处理队列中的 任务，并且要中断正在处理的任务。</li><li><code>TIDYING</code>：所有的任务都已终止了，workerCount 为 0，线程池进入该状态后会调 terminated() 方法进入 TERMINATED 状态。</li><li><code>TERMINATED</code>：终止状态，terminated() 方法调用结束后的状态。</li></ul><p>线程池关闭</p><ul><li>shutdown：将线程池状态设置为SHUTDOWN并且中断掉所有空闲线程</li><li>shutdownNow：将线程池状态设置为STOP，尝试停止所有线程，不论是否在执行任务，返回等待任务列表</li></ul><h3 id="_4-volatile" tabindex="-1">4.volatile <a class="header-anchor" href="#_4-volatile" aria-label="Permalink to &quot;4.volatile&quot;">​</a></h3><ul><li>定义：是一个修饰变量的关键字，保证了多处理器开发中共享变量的“可见性”。可见性是指一个线程修改了共享变量的值，另一个线程也能读到这个修改后的值，它不会引起线程上下文切换和调度。</li><li>原理：被volatile修饰的变量在写操作时会多出一条lock前缀指令，这个指令会引起处理器缓存回写到内存；这个回写操作会导致其他处理器的缓存失效，其他处理器下一次读取变量时会强制从内存中读取。这样一个原理保证了被volatile修饰的变量的可见性。</li></ul><h3 id="_5-synchronized" tabindex="-1">5.synchronized <a class="header-anchor" href="#_5-synchronized" aria-label="Permalink to &quot;5.synchronized&quot;">​</a></h3><ul><li><p>synchronized是一个关键字，是用来实现代码同步，保证线程安全的。</p></li><li><p>用法</p><ul><li>修饰成员方法，锁的是当前实例对象</li><li>修饰静态方法，锁是当前类的Class对象</li><li>修饰代码块时，锁是sychronized括号配置的对象</li></ul></li><li><p>原理</p><p>JVM是基于进入和退出Monitor对象来实现sychronized的同步机制。Java的每个对象都有一个monitor和它关联。同时还提供了monitorenter和monitorexit两个指令，这两个指令是配对出现的。monitorenter指令是在编译后插入到同步的开始位置，monitorexit插入到同步的结束位置。当一个线程这行到monitorenter指令后，会尝试获取对象所对应的monitor的所有权，获取到了就执行同步代码，没有获取到就阻塞等待，以这样的方式来实现代码同步。</p><p>synchronized用的锁是存在Java对象头里面的。</p></li><li><p>锁优化</p><p>无锁状态-&gt;偏向锁-&gt;轻量级锁-&gt;重量级锁</p><p>偏向锁是加锁和解锁不需要额外消耗，如果线程间存在锁竞争，会带来额外的锁撤销消耗。</p><p>轻量级锁中竞争的线程不会阻塞，程序的响应速度变快。如果始终得不到锁竞争的线程，使用自旋会消耗CPU。</p><p>重量级锁中线程竞争不使用自旋，不会消耗CPU，获取不到锁的线程会直接进入阻塞状态，响应时间变长。</p></li></ul><h3 id="_6-cas" tabindex="-1">6.CAS <a class="header-anchor" href="#_6-cas" aria-label="Permalink to &quot;6.CAS&quot;">​</a></h3><ul><li><p>原子操作：不能被中断的一个或一系列操作</p></li><li><p>定义</p><p>CAS是实现原子操作的一种方法，Compare And Swap，CAS操作需要输入两个值，一个旧值，一个新值。在操作期间先比较一下旧值有没有发生变化，如果没有发生变化，则交换成新值；如果发生变化了，则不进行交换。</p></li><li><p>实现原理</p><p>Java中的CAS操作时利用了处理器提供的CMPXCHG指令实现的。</p></li><li><p>结合原子类</p><p>原子更新基本数据类型</p><ul><li>AtomicBoolean、AtomicInteger、AtomicLong</li></ul><p>原子更新数组类型</p><ul><li>AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray</li></ul><p>原子更新引用类型</p><ul><li>AtomicReference</li></ul></li><li><p>CAS的副作用</p><ul><li>ABA问题-&gt;解决方案在变量前追加版本号</li><li>循环时间长开销大</li><li>只能保证一个共享变量的原子操作-&gt;JDK提供了AtomicReference来保证引用对象之间的原子性，可以把多个变量放在一个对象里来进行CAS操作</li></ul></li></ul><h3 id="_7-aqs" tabindex="-1">7.AQS <a class="header-anchor" href="#_7-aqs" aria-label="Permalink to &quot;7.AQS&quot;">​</a></h3><p>AQS是AbstractQueuedSynchronizer的缩写，中文翻译是抽象队列同步器，它是Java并发包中的一个核心组件，它为构建锁和其他同步组件提供一个可靠的框架。*它定义了一系列方法来访问和修改同步状态，我们在使用AQS来实现自己的同步器要继承这个类，并实现自己的获取和释放方法。*比如Semaphore、CyclicBarrier、CountDownLatch、ReentrantLock等都是AQS框架实现的。</p><p>AQS内部是维护一个int类型的同步状态，并维护了一个等待队列来管理那些获取资源失败的线程。当一个线程尝试获取资源而失败时，它会被加入到这个队列的尾部，并且在队列中等待。直到前驱节点（通常是当前持有资源的线程）释放资源，这个线程才会尝试再次获取资源。</p><p>我们可以用AQS来实现独占模式的同步组件，比如JDK中的ReentrantLock，它每次只允许一个线程持有锁，其他线程都无法获取锁，直到持有锁的线程将锁释放</p><p>还可以实现共享模式的同步器，比如ReadWriteLock的读锁，就是共享模式工作，在没有线程持有写锁的情况下，允许多个线程持有读锁</p><p><a href="https://mp.weixin.qq.com/s/jEx-4XhNGOFdCo4Nou5tqg" target="_blank" rel="noreferrer">https://mp.weixin.qq.com/s/jEx-4XhNGOFdCo4Nou5tqg</a></p><h3 id="_8-锁分类" tabindex="-1">8.锁分类 <a class="header-anchor" href="#_8-锁分类" aria-label="Permalink to &quot;8.锁分类&quot;">​</a></h3><h4 id="悲观锁" tabindex="-1">悲观锁 <a class="header-anchor" href="#悲观锁" aria-label="Permalink to &quot;悲观锁&quot;">​</a></h4><p>悲观锁的核心思想是对数据被外界修改持保守态度。它假设数据很可能会被其他线程修改，因此在数据处理过程中通过加锁来防止其他线程对数据进行修改。</p><h4 id="乐观锁" tabindex="-1">乐观锁 <a class="header-anchor" href="#乐观锁" aria-label="Permalink to &quot;乐观锁&quot;">​</a></h4><p>乐观锁机制采取了一种宽松的加锁机制。它假设数据通常情况下不会造成冲突，因此在数据提交更新时才会检查是否有其他线程也尝试修改了这个数据。</p><p>在Java编程中，乐观锁可以通过原子变量类（如AtomicInteger）来实现，或者通过CAS（比较并交换）操作来实现无锁的线程安全编程。</p><h4 id="公平锁" tabindex="-1">公平锁 <a class="header-anchor" href="#公平锁" aria-label="Permalink to &quot;公平锁&quot;">​</a></h4><p>朴素的公平就是先进先出，公平锁就是竞争锁的时候要排队</p><h4 id="非公平锁" tabindex="-1">非公平锁 <a class="header-anchor" href="#非公平锁" aria-label="Permalink to &quot;非公平锁&quot;">​</a></h4><p>锁的竞争是非公平的，后来的线程也可以直接参与锁竞争</p><h4 id="独占锁" tabindex="-1">独占锁 <a class="header-anchor" href="#独占锁" aria-label="Permalink to &quot;独占锁&quot;">​</a></h4><p>就有一个线程能持有锁，其他线程要阻塞等待，直到持有锁的线程释放锁后，其他线程再重新竞争</p><h4 id="共享锁" tabindex="-1">共享锁 <a class="header-anchor" href="#共享锁" aria-label="Permalink to &quot;共享锁&quot;">​</a></h4><p>锁一个被多个线程持有，典型的就是ReadWriteLock的读锁，在没有线程持有写锁时，读锁可以被多个线程持有</p><h3 id="_9-同步器" tabindex="-1">9.同步器 <a class="header-anchor" href="#_9-同步器" aria-label="Permalink to &quot;9.同步器&quot;">​</a></h3><h4 id="reentrantlock" tabindex="-1">ReentrantLock <a class="header-anchor" href="#reentrantlock" aria-label="Permalink to &quot;ReentrantLock&quot;">​</a></h4><p>独占锁，可重入</p><h4 id="semaphore" tabindex="-1">Semaphore <a class="header-anchor" href="#semaphore" aria-label="Permalink to &quot;Semaphore&quot;">​</a></h4><p>允许多个线程同时进入临界区，进入临界区的线程数量是可以控制的</p><h4 id="cyclicbarrier" tabindex="-1">CyclicBarrier <a class="header-anchor" href="#cyclicbarrier" aria-label="Permalink to &quot;CyclicBarrier&quot;">​</a></h4><p>CyclicBarrier让线程在一个屏障上等待，然后执行同步快，然后分开执行，然后进入下一个屏障</p><ul><li>CountDownLatch</li><li>Phaser</li><li>Exchanger</li><li>ReentrantReadWriteLock</li></ul><h3 id="_10-并发容器" tabindex="-1">10.并发容器 <a class="header-anchor" href="#_10-并发容器" aria-label="Permalink to &quot;10.并发容器&quot;">​</a></h3><h4 id="list" tabindex="-1">List <a class="header-anchor" href="#list" aria-label="Permalink to &quot;List&quot;">​</a></h4><ul><li><p>CopyOnWriteArrayList</p><blockquote><p>主要基于写时复制的原理来实现线程安全，对于读多写少场景适用。</p><p>写与写之间使用synchronized实现同步</p></blockquote></li></ul><h4 id="map" tabindex="-1">Map <a class="header-anchor" href="#map" aria-label="Permalink to &quot;Map&quot;">​</a></h4><ul><li><p>ConcurrentHashMap</p><blockquote><p>1.分段锁到细粒度锁的转变</p><p>2.链表转红黑树</p><p>3.并发级别的动态调整</p><p>4.数据结构优化</p><p>5.计数器优化：LongAdder</p><ul><li>优化并发下累加操作而设计的</li><li>采用分段累加的策略</li></ul></blockquote></li><li><p>ConcurrentSkipListMap</p><blockquote><p>1.基于跳表这个数据结构实现的线程安全的Map</p><p>2.Key要实现Comparable接口，或者传入Comparator，也就是说key要是可以比较的</p><p>3.除了提供了Map的标志接口外，还提供了NavigableMap接口的方法</p></blockquote></li></ul><h4 id="set" tabindex="-1">Set <a class="header-anchor" href="#set" aria-label="Permalink to &quot;Set&quot;">​</a></h4><ul><li><p>CopyOnWriteArraySet</p><blockquote><p>基于CopyOnWriteArrayList实现的</p></blockquote></li></ul><h3 id="_11-并发队列" tabindex="-1">11.并发队列 <a class="header-anchor" href="#_11-并发队列" aria-label="Permalink to &quot;11.并发队列&quot;">​</a></h3><h4 id="什么事阻塞队列" tabindex="-1">什么事阻塞队列 <a class="header-anchor" href="#什么事阻塞队列" aria-label="Permalink to &quot;什么事阻塞队列&quot;">​</a></h4><p>阻塞队列是队列的一种特殊实现，它支持阻塞的插入和移除。在Java中阻塞队列一般的入队方法有add，put，offer三个，出对方法有remove，take，poll三个，其中add/remove采用抛出异常的机制处理对满和对空，put/take采用阻塞方式处理对满和对空，offer/poll采用返回特殊值的方式处理对满和对空，offer对满返回false，poll对空返回null。offer(time)/poll(time)阻塞指定时间后返回</p><h4 id="arrayblockingqueue" tabindex="-1">ArrayBlockingQueue <a class="header-anchor" href="#arrayblockingqueue" aria-label="Permalink to &quot;ArrayBlockingQueue&quot;">​</a></h4><p>基于数组实现的有界阻塞队列，是按照先进先出原则，默认情况下是不公平访问队列。</p><h4 id="linkedblockingqueue" tabindex="-1">LinkedBlockingQueue <a class="header-anchor" href="#linkedblockingqueue" aria-label="Permalink to &quot;LinkedBlockingQueue&quot;">​</a></h4><p>基于链表实现的阻塞队列，非公平访问队列</p><h4 id="priorityblockingqueue" tabindex="-1">PriorityBlockingQueue <a class="header-anchor" href="#priorityblockingqueue" aria-label="Permalink to &quot;PriorityBlockingQueue&quot;">​</a></h4><p>是一个支持优先级的阻塞队列，默认情况下是采取自然顺序生序排列（小顶堆），不能保证同优先级元素的顺序。非公平访问策略</p><h4 id="delayqueue" tabindex="-1">DelayQueue <a class="header-anchor" href="#delayqueue" aria-label="Permalink to &quot;DelayQueue&quot;">​</a></h4><p>是一个支持延迟获取元素的无界队列，基于PriorityQueue来实现的，队列中的元素必须实现Delayed接口，创建元素时，可以指定延迟多久，只有到达指定延迟时间才能从队列中提取元素。非公平访问策略</p><h4 id="synchronousqueue" tabindex="-1">SynchronousQueue <a class="header-anchor" href="#synchronousqueue" aria-label="Permalink to &quot;SynchronousQueue&quot;">​</a></h4><p>不存储元素的阻塞队列，每个put操作必须等待一个take操作，否则不能继续添加元素。默认是非公平访问策略</p><h4 id="linkedtransferqueue" tabindex="-1">LinkedTransferQueue <a class="header-anchor" href="#linkedtransferqueue" aria-label="Permalink to &quot;LinkedTransferQueue&quot;">​</a></h4><p>基于链表实现的无界阻塞队列，相对于其他阻塞队列，多了transfer和tryTransfer方法。</p><p>transfer</p><ul><li>有消费者阻塞等待，则直接将元素交给阻塞的消费者</li><li>没有消费者阻塞等待，则将元素放入对尾，等到该元素被消费者消费后，再返回</li></ul><p>tryTransfer</p><ul><li>有消费者等待，则直接给消费者，然后返回true</li><li>没有消费者，则直接返回false</li></ul><p>tryTransfer(timeout)：在tryTransfer基础上，没有消费者等待时，等待指定时间再返回，返回时若元素还未被消费则返回false</p><h4 id="linkedblockingdeque" tabindex="-1">LinkedBlockingDeque <a class="header-anchor" href="#linkedblockingdeque" aria-label="Permalink to &quot;LinkedBlockingDeque&quot;">​</a></h4><p>是一个由链表结构组成的双向阻塞队列。双向队列是指可以在队列的两段插入和移除元素。</p><h4 id="阻塞队列的实现原理" tabindex="-1">阻塞队列的实现原理 <a class="header-anchor" href="#阻塞队列的实现原理" aria-label="Permalink to &quot;阻塞队列的实现原理&quot;">​</a></h4><ul><li>使用通知模式。消费者通知生成者，生产者通知消费者</li></ul><h1 id="java基础" tabindex="-1">Java基础 <a class="header-anchor" href="#java基础" aria-label="Permalink to &quot;Java基础&quot;">​</a></h1><h2 id="_1-arrays-sort" tabindex="-1">1.Arrays.sort() <a class="header-anchor" href="#_1-arrays-sort" aria-label="Permalink to &quot;1.Arrays.sort()&quot;">​</a></h2><p>Collections.sort()调用List.sort()，List.sort()是将list转换为数组后调用Arrays.sort()</p><p>Arrays.sort()对对象排序使用改进后的归并排序（TimSort），也可以通过参数配置，强制使用经典归并排序。</p><p>TimSort是归并排序和插入排序的结合体，首先把数组分为小块，然后每个小块再使用插入排序，最后使用归并排序将这些已排序的小块合并成较大的有序块</p><p>Arrays.sort()对基本数据类型数组排序使用双轴快排</p><ul><li>数组长度大于4096 使用并行排序</li><li>数组长度小于65使用混合插入排序</li><li>数组长度小于44使用插入排序</li><li>数组长度大于384使用堆排序</li></ul><h2 id="_2-java的注解" tabindex="-1">2.Java的注解 <a class="header-anchor" href="#_2-java的注解" aria-label="Permalink to &quot;2.Java的注解&quot;">​</a></h2><p>Java注解是一种代码级别的说明，它们可以被编译器或运行时环境用来进行代码分析或执行某些处理。</p><p>注解对于改善代码的可读性、为工具提供配置信息以及在编译期或运行时进行特定处理非常有用。</p><p>比如java自带注解</p><ul><li>@Override：指定方法覆盖了父类方法，如果父类没有这个方法，编译器会报错</li><li>@Deprecated：标记过时的方法或类</li><li>@SuppressWarnings：指示编译器忽略特定的警告</li></ul><p>声明一个注解需要使用@interface，再配合java的提供的四个元注解来自己实现一个注解</p><h3 id="元注解" tabindex="-1">元注解 <a class="header-anchor" href="#元注解" aria-label="Permalink to &quot;元注解&quot;">​</a></h3><ul><li>@Target：标记注解的作用范围，比如有：ElementType.PACKAGE，ElementType.TYPE（也就是class），ElementType.METHOD，ElementType.PARAMETER</li></ul><h1 id="spring" tabindex="-1">Spring <a class="header-anchor" href="#spring" aria-label="Permalink to &quot;Spring&quot;">​</a></h1><h2 id="_1-spring是什么" tabindex="-1">1.Spring是什么 <a class="header-anchor" href="#_1-spring是什么" aria-label="Permalink to &quot;1.Spring是什么&quot;">​</a></h2><p>是一个开源的Java开发框架，旨在提高开发人员的效率和系统的可维护性。</p><p>其中有两个核心特性是支持控制反转和面向切面编程。</p><h2 id="_2-spring-模块" tabindex="-1">2.Spring 模块 <a class="header-anchor" href="#_2-spring-模块" aria-label="Permalink to &quot;2.Spring 模块&quot;">​</a></h2><h3 id="core-container-核心模块" tabindex="-1">Core Container 核心模块 <a class="header-anchor" href="#core-container-核心模块" aria-label="Permalink to &quot;Core Container 核心模块&quot;">​</a></h3><ul><li>spring-core：Spring框架的核心工具类</li><li>spring-bean：提供对Bean的创建，配置和管理的功能支持</li><li>spring-context：提供对国际化，事件传播，资源加载等功能的支持</li><li>spring-expression：提供对表达式语言SpEL的支持，该模块仅依赖core模块</li></ul><h3 id="aop模块" tabindex="-1">AOP模块 <a class="header-anchor" href="#aop模块" aria-label="Permalink to &quot;AOP模块&quot;">​</a></h3><ul><li>spring-aspects：为AspectJ的集成提供支持</li><li>spring-aop：提供面向切面编程的实现</li><li>spring-instrument：提供了为 JVM 添加代理（agent）的功能</li></ul><p>Data Access/Integration 数据访问与集成</p><ul><li>spring-jdbc：提供对数据库访问的抽象jdbc</li><li>spring-tx：提供对事务的支持</li><li>spring-orm：提供对ORM（数据持久化）框架的支持</li><li>spring-jms：消息服务</li></ul><p>Spring Web</p><ul><li>spring-web：对web功能实现提供了基础支持</li><li>spring-webmvc：提供了对Spring MVC的实现</li><li>spring-websocket：提供了对WebSocket的支持，可以让客户端和服务端双向通信</li><li>spring-webflux：提供了对webflux的支持。webflux是spring5.0以后引入的响应式框架，与Mvc不同它不需要Servlet API是完全异步的</li></ul><p>Messaging</p><ul><li>spring-messaging：spring4开始加入的模块，主要职责是为spring框架集成一些基础的报文传送应用</li></ul><p>Spring Test</p><ul><li>spring-test提供了对JUnit，TestNG，Mockito，PowerMock等常用测试框架的支持</li></ul><h2 id="_3-spring-ioc" tabindex="-1">3.Spring IOC <a class="header-anchor" href="#_3-spring-ioc" aria-label="Permalink to &quot;3.Spring IOC&quot;">​</a></h2><p>IOC是控制反转，是一种设计思想，用于减少代码之间的耦合。</p><p>控制反转将创建依赖对象的控制权交给外部容器，使得代码之间的耦合度降低</p><p>依赖注入是IOC的一种实现方式，在Spring框架中对象不需要自己查找依赖和创建依赖对象，而是通过构造器、setter方法或者字段反射注入，这些依赖对象会由spring的IOC容器来创建和管理</p><h2 id="_4-spring-bean" tabindex="-1">4.Spring Bean <a class="header-anchor" href="#_4-spring-bean" aria-label="Permalink to &quot;4.Spring Bean&quot;">​</a></h2><h3 id="将一个类标记为bean的注解" tabindex="-1">将一个类标记为Bean的注解 <a class="header-anchor" href="#将一个类标记为bean的注解" aria-label="Permalink to &quot;将一个类标记为Bean的注解&quot;">​</a></h3><ul><li>@Component：通用注解，可以标记一个类为spring的组件</li><li>@Controller：标记为MVC的控制层，主要接收用户请求，调用service方法，将结果返回给用户</li><li>@Service：对应服务层，主要处理一些复杂逻辑</li><li>@Repository：对应持久层，主要用于数据库相关操作</li></ul><h3 id="component和-bean的区别" tabindex="-1">@Component和@Bean的区别 <a class="header-anchor" href="#component和-bean的区别" aria-label="Permalink to &quot;@Component和@Bean的区别&quot;">​</a></h3><ul><li>@Component注解在类上，@Bean注解在方法上</li><li>@Component表示这个类可以被spring 容器实例化并管理；@Bean将一个方法的返回值作为spring的bean进行管理</li><li>@Bean更加灵活，对于一些依赖的第三方包需要让spring管理时，可以使用@Bean来进行对象构造然后交由spring管理。@Component是依赖Spring自己对这个类进行实例化，然后再进行管理</li></ul><h3 id="autowired和-resource" tabindex="-1">@Autowired和@Resource <a class="header-anchor" href="#autowired和-resource" aria-label="Permalink to &quot;@Autowired和@Resource&quot;">​</a></h3><ul><li>@Autowired是spring提供的，@Resource是java技术规范提供</li><li>@Autowired是默认byType来进行自动装配，如果需要用名字，要结合@Qualifier注解一起使用。 @Resource是默认byName进行装配，如果找不到与名称匹配的bean则回退到按类型装配</li><li>依赖项是否必须存在？@Autowired通过required属性配置。@Resource必须存在</li><li>注解位置：@Autowired可以用在构造器、字段、方法以及参数上，@Resource不能用在构造器上和参数上</li></ul><h3 id="spring-bean作用域" tabindex="-1">Spring Bean作用域 <a class="header-anchor" href="#spring-bean作用域" aria-label="Permalink to &quot;Spring Bean作用域&quot;">​</a></h3><ul><li><strong>singleton</strong>：单例模式，Ioc容器中只有一个唯一的bean</li><li><strong>prototype</strong>：多例，每次getBean会返回一个新的对象</li></ul><h3 id="spring-bean的线程安全" tabindex="-1">Spring Bean的线程安全 <a class="header-anchor" href="#spring-bean的线程安全" aria-label="Permalink to &quot;Spring Bean的线程安全&quot;">​</a></h3><ul><li>prototype模式的bean不存在线程安全问题</li><li>singleton模式的bean，在bean是无状态实例的情况下，也没有线程安全问题</li><li>对于有状态singleton可以使用，ThreadLocal来保存状态，或者使用线程安全同步的办法</li></ul><h3 id="spring-bean生命周期" tabindex="-1">Spring Bean生命周期 <a class="header-anchor" href="#spring-bean生命周期" aria-label="Permalink to &quot;Spring Bean生命周期&quot;">​</a></h3><ul><li>创建实例：通过反射创建实例对象</li><li>填充属性：填充依赖，注入的其他属性</li><li>初始化：先检测Aware接口，依次执行，然后执行BeanPostProcessor前置处理，然后检测初始化方法（InitializingBean或者init-method）并执行，然后执行BeanPostProcessor的后置处理</li><li>销毁：并不是立即销毁，先将注册的销毁方法检测到并记录下来（DisposableBean或者destroy-method），然后等到bean使用完后，真正要销毁时，再调用销毁方法来销毁bean</li></ul><h2 id="_5-spring-aop" tabindex="-1">5.Spring AOP <a class="header-anchor" href="#_5-spring-aop" aria-label="Permalink to &quot;5.Spring AOP&quot;">​</a></h2><h3 id="回答流程" tabindex="-1">回答流程 <a class="header-anchor" href="#回答流程" aria-label="Permalink to &quot;回答流程&quot;">​</a></h3><ul><li>先说是什么？面向切面编程，功能性需求和非功能性需求解耦</li><li>主要主要概念</li><li>动态代理是怎么做的</li><li>我们通常在spring开发一个aop的操作步骤</li></ul><h3 id="概念" tabindex="-1">概念 <a class="header-anchor" href="#概念" aria-label="Permalink to &quot;概念&quot;">​</a></h3><ul><li>连接点（JoinPoint）：目标对象所属类中定义的所有方法</li><li>切入点（Pointcut）：被拦截或者的连接点</li><li>通知（Advice）：增强的逻辑</li><li>切面（Aspect）：切入点和通知共同组成的产物叫切面</li><li>织入（Weaving）：将通知应用到目标对象上，进而生成代理对象的过程叫织入</li></ul><h3 id="aop如何开发" tabindex="-1">AOP如何开发 <a class="header-anchor" href="#aop如何开发" aria-label="Permalink to &quot;AOP如何开发&quot;">​</a></h3><ul><li><p>确定拦截的目标</p><blockquote><p>通俗的讲就是要确定拦截什么类的什么方法（统配、注解等）</p></blockquote></li><li><p>创建切面类</p><blockquote><p>定义一个切面类，加上@Aspect注解声明为切面，然后通常再使用@Component将切面注册到IOC容器，交由spring管理</p></blockquote></li><li><p>定义切点</p><blockquote><p>使用@PointCut定义切点，结合正则式或注解来</p></blockquote></li></ul><h3 id="动态代理" tabindex="-1">动态代理 <a class="header-anchor" href="#动态代理" aria-label="Permalink to &quot;动态代理&quot;">​</a></h3><ul><li>JDK动态代理</li><li>Cglib动态代理</li></ul><h2 id="_6-spring-事务" tabindex="-1">6.Spring 事务 <a class="header-anchor" href="#_6-spring-事务" aria-label="Permalink to &quot;6.Spring 事务&quot;">​</a></h2><h3 id="管理事务的方式" tabindex="-1">管理事务的方式 <a class="header-anchor" href="#管理事务的方式" aria-label="Permalink to &quot;管理事务的方式&quot;">​</a></h3><ul><li>编程式事务：通过编写代码，使用TransactionTemplate或者TransactionManager来实现</li><li>声明式事务：通过@Transactional注解来实现，底层是基于AOP实现的</li></ul><h3 id="事务传播方式" tabindex="-1">事务传播方式 <a class="header-anchor" href="#事务传播方式" aria-label="Permalink to &quot;事务传播方式&quot;">​</a></h3><ul><li><p><strong><code>TransactionDefinition.PROPAGATION_REQUIRED</code></strong></p><blockquote><p>默认方式，如果当前存在事务，则加入该事务；如果当前没有事务，则创建一个新的事务</p></blockquote></li><li><p><strong><code>TransactionDefinition.PROPAGATION_REQUIRES_NEW</code></strong></p><blockquote><p>创建一个新的事务，如果当前存在事务，则把当前事务挂起</p></blockquote></li><li><p><strong><code>TransactionDefinition.PROPAGATION_NESTED</code></strong></p><blockquote><p>如果当前存在事务，则创建一个事务作为当前事务的嵌套事务来运行</p></blockquote></li><li><p><strong><code>TransactionDefinition.PROPAGATION_MANDATORY</code></strong></p><blockquote><p>如果当前存在事务，则加入该事务；如果当前没有事务，则抛出异常。</p></blockquote></li><li><p><strong><code>TransactionDefinition.PROPAGATION_SUPPORTS</code></strong></p><blockquote><p>如果当前存在事务，则加入该事务；如果当前没有事务，则以非事务的方式继续运行</p></blockquote></li><li><p><strong><code>TransactionDefinition.PROPAGATION_NOT_SUPPORTED</code></strong></p><blockquote><p>以非事务方式运行，如果当前存在事务，则把当前事务挂起。</p></blockquote></li><li><p><strong><code>TransactionDefinition.PROPAGATION_NEVER</code></strong></p><blockquote><p>以非事务方式运行，如果当前存在事务，则抛出异常</p></blockquote></li></ul><p>事务隔离级别</p><ul><li><p><strong><code>TransactionDefinition.ISOLATION_DEFAULT</code></strong></p><blockquote><p>使用后端数据库默认的隔离级别，MySQL 默认采用的 <code>REPEATABLE_READ</code> 隔离级别 Oracle 默认采用的 <code>READ_COMMITTED</code> 隔离级别</p></blockquote></li><li><p><strong><code>TransactionDefinition.ISOLATION_READ_UNCOMMITTED</code></strong></p><blockquote><p>读未提交。脏读，幻读，不可重复读</p></blockquote></li><li><p><strong><code>TransactionDefinition.ISOLATION_READ_COMMITTED</code></strong></p><blockquote><p>读已提交。幻读，不可重复读</p></blockquote></li><li><p><strong><code>TransactionDefinition.ISOLATION_REPEATABLE_READ</code></strong></p><blockquote><p>可重复读。可能幻读（mysql间隙锁，next-key Lock）</p></blockquote></li><li><p><strong><code>TransactionDefinition.ISOLATION_SERIALIZABLE</code></strong></p><blockquote><p>串行化。串行执行</p></blockquote></li></ul><h3 id="transactional注解" tabindex="-1">@Transactional注解 <a class="header-anchor" href="#transactional注解" aria-label="Permalink to &quot;@Transactional注解&quot;">​</a></h3><ul><li>写在类上，则该类的所有public方法，都具有该类型的事务属性</li><li>写在方法上，则该方法具有该事务属性，可以覆盖类上的事务属性</li><li>默认在RuntimeException和Error下会回滚</li><li>使用rollbackFor指定哪些异常回滚，noRollbackFor指定哪些异常不回滚</li></ul><h2 id="_7-spring-mvc" tabindex="-1">7.Spring MVC <a class="header-anchor" href="#_7-spring-mvc" aria-label="Permalink to &quot;7.Spring MVC&quot;">​</a></h2><h3 id="核心组件" tabindex="-1">核心组件 <a class="header-anchor" href="#核心组件" aria-label="Permalink to &quot;核心组件&quot;">​</a></h3><ul><li><p><strong><code>DispatcherServlet</code></strong></p><blockquote><p>核心调度中心，负责请求接收、分发，并给予客户端响应</p></blockquote></li><li><p><strong><code>HandlerMapping</code></strong></p><blockquote><p>处理器映射。可以根据URL查找到可以处理该请求的handler，并将请求涉及到的拦截器和handler一起封装</p></blockquote></li><li><p><strong><code>HandlerAdapter</code></strong></p><blockquote><p>处理器适配器。适配执行对应的handler</p></blockquote></li><li><p><strong><code>Handler</code></strong></p><blockquote><p>请求处理器。处理实际请求</p></blockquote></li><li><p><strong><code>ViewResolver</code></strong></p><blockquote><p>视图解析器。根据Handler返回的逻辑视图，解析并渲染真正的视图，并传递给DispatcherServlet响应给客户端</p></blockquote></li></ul><p>MVC工作原理</p><ol><li>客户端（浏览器）发送请求， <code>DispatcherServlet</code>接收请求。</li><li><code>DispatcherServlet</code> 根据请求信息调用 <code>HandlerMapping</code> 。<code>HandlerMapping</code> 根据 URL 去匹配查找能处理该请求的 <code>Handler</code> ，并会将请求涉及到的拦截器和 <code>Handler</code> 一起封装。</li><li><code>DispatcherServlet</code> 调用 <code>HandlerAdapter</code>适配器执行 <code>Handler</code> 。</li><li><code>Handler</code> 完成对用户请求的处理后，会返回一个 <code>ModelAndView</code> 对象给<code>DispatcherServlet</code>，<code>ModelAndView</code> 包含了数据模型以及相应的视图的信息。<code>Model</code> 是返回的数据对象，<code>View</code> 是个逻辑上的 <code>View</code>。</li><li><code>ViewResolver</code> 会根据逻辑 <code>View</code> 查找实际的 <code>View</code></li><li><code>DispaterServlet</code> 把返回的 <code>Model</code> 传给 <code>View</code>，进行视图渲染</li><li>把<code>View</code>返回给浏览器</li></ol><h3 id="统一异常处理" tabindex="-1">统一异常处理 <a class="header-anchor" href="#统一异常处理" aria-label="Permalink to &quot;统一异常处理&quot;">​</a></h3><ul><li>@ControllerAdvice注解，@RestControllerAdvice</li><li>@ExceptionHandler</li><li>底层是用AOP实现</li><li><code>ExceptionHandlerMethodResolver</code> 中 <code>getMappedMethod</code> 方法决定了异常具体被哪个被 <code>@ExceptionHandler</code> 注解修饰的方法处理异常</li></ul><h2 id="_8-spring-boot" tabindex="-1">8.Spring Boot <a class="header-anchor" href="#_8-spring-boot" aria-label="Permalink to &quot;8.Spring Boot&quot;">​</a></h2><h3 id="spring-boot-starter" tabindex="-1">Spring Boot Starter <a class="header-anchor" href="#spring-boot-starter" aria-label="Permalink to &quot;Spring Boot Starter&quot;">​</a></h3><ul><li><p>Spring Boot Staters是一系列依赖关系的集合，</p><p>在没有Spring Boot Starters之前，我们开发REST服务或Web应用程序时；我们需要使用像Spring MVC,Tomcat和Jackson这样的库，这些依赖我们需要手动一个一个添加。但是，有了 Spring Boot Starters我们只需要一个只需添加一个spring-boot-starter--web一个依赖就可以了，这个依赖包含的子依赖中包含了我们开发REST服务需要的所有依赖。</p></li></ul><h3 id="spring-boot支持内置servlet容器" tabindex="-1">Spring Boot支持内置Servlet容器 <a class="header-anchor" href="#spring-boot支持内置servlet容器" aria-label="Permalink to &quot;Spring Boot支持内置Servlet容器&quot;">​</a></h3><ul><li>Tomcat</li><li>Jetty</li></ul><p>Spring Boot（spring-boot-starter-web)使用Tomcat作为默认的嵌入式servlet容器，如果想使用Jety的话只需要修改pom.xml(Maven)或者build.gradle(Gradle)就可以了。在Maven中引入``spring-boot-starter-web<code>时exclusion</code>spring-boot-starter-tomcat<code>,然后再引入依赖</code>spring-boot-starter-jetty`。</p><h3 id="springbootapplication-注解" tabindex="-1">@SpringBootApplication 注解 <a class="header-anchor" href="#springbootapplication-注解" aria-label="Permalink to &quot;@SpringBootApplication 注解&quot;">​</a></h3><ul><li><code>@SpringBootApplication</code>主要是<code>@Configuration</code>,<code>@EnableAutoConfiguration</code>,<code>@ComponentScan</code>的集合。 <ul><li><code>@Configuration</code>： 允许在上下文中注册额外的Bean或者使用其他配置类</li><li><code>@EnableAutoConfiguration</code>：启动SpringBoot的自动装配机制</li><li><code>@ComponentScan</code>：扫描被Spring管理的bean，默认扫描被注解的类所在的包下的所有类</li></ul></li></ul><h3 id="springboot自动装配" tabindex="-1">SpringBoot自动装配 <a class="header-anchor" href="#springboot自动装配" aria-label="Permalink to &quot;SpringBoot自动装配&quot;">​</a></h3><p>自动装配的核心是，本应该由开发者编写的配置，转为框架自动根据项目中整合的场景依赖，合理地做出判断并装配合适的Bean到IOC容器中。</p><ul><li><code>@SpringBootApplication</code>注解</li><li><code>@EnableAutoConfiguration</code>是自动装配的核心注解，自动装配的实际执行者，它会导入<code>AutoConfigurationImportSelector</code>类，这个类会读取类路径上的<code>META-INF/spring.factories</code>文件，此文件中列出了所有的自动配置类。自动配置通常带有<code>@Configuration</code>注解，包含一系列的<code>@Bean</code>方法，这些方法会根据条件来创建和配置Bean。这些条件可以是类路径上是否存在某个类、某个Bean是否已经被注册、某个配置的属性值等。大量使用了条件注解，如<code>@ConditionalOnClass</code>,<code>@ConditionalOnMissingBean</code>,<code>@ConditionalOnProperty</code>等，这些注解确保了只有在特定条件满足时，相应的自动配置类或者<code>@Bean</code>方法才会被激活，被包含中最终的应用上下文中。开发者配置的Bean会优先于自动配置的Bean。</li></ul><h3 id="springboot读取配置文件" tabindex="-1">SpringBoot读取配置文件 <a class="header-anchor" href="#springboot读取配置文件" aria-label="Permalink to &quot;SpringBoot读取配置文件&quot;">​</a></h3><h3 id="springboot加载配置文件的优先级" tabindex="-1">SpringBoot加载配置文件的优先级 <a class="header-anchor" href="#springboot加载配置文件的优先级" aria-label="Permalink to &quot;SpringBoot加载配置文件的优先级&quot;">​</a></h3><h3 id="springboot监控系统运行状态" tabindex="-1">SpringBoot监控系统运行状态 <a class="header-anchor" href="#springboot监控系统运行状态" aria-label="Permalink to &quot;SpringBoot监控系统运行状态&quot;">​</a></h3><ul><li>引入依赖<code>spring-boot-starter-actuator</code></li><li><code>/health</code></li></ul><h1 id="mybatis" tabindex="-1">Mybatis <a class="header-anchor" href="#mybatis" aria-label="Permalink to &quot;Mybatis&quot;">​</a></h1><h2 id="和-区别" tabindex="-1">${}和#{} 区别 <a class="header-anchor" href="#和-区别" aria-label="Permalink to &quot;${}和#{} 区别&quot;">​</a></h2><ul><li>${}静态文本占位符，可以理解为直接进行字符串拼接替换</li><li>#{}被解析成Sql参数占位符。</li></ul><h2 id="mybatis标签" tabindex="-1">Mybatis标签 <a class="header-anchor" href="#mybatis标签" aria-label="Permalink to &quot;Mybatis标签&quot;">​</a></h2><ul><li>select、update、delete、insert</li><li>resultMap、parameterMap、sql、include、selectKey</li><li>where、trim、foreach、if、choose、when、otherwise</li></ul><h2 id="dao接口工作原理" tabindex="-1">Dao接口工作原理 <a class="header-anchor" href="#dao接口工作原理" aria-label="Permalink to &quot;Dao接口工作原理&quot;">​</a></h2><ul><li>接口的全限定名，就是映射文件的namespace，接口的方法对应的映射文件中的MappedStatement的id，接口的入参就是传递给Sql的参数</li><li>接口的全限定名加方法名拼接成一个字符串作为key，可以确定一个唯一的MappedStatement，所以不能重载</li><li>Dao接口的实现是框架使用动态代理为其生成一个代理对象，当调用接口的方法时，代理对象会拦截调用，然后找到与该方法匹配的MappedStatement，并执行对应的Sql，然后再将Sql执行结果返回</li></ul><h2 id="mybtais的分页" tabindex="-1">Mybtais的分页 <a class="header-anchor" href="#mybtais的分页" aria-label="Permalink to &quot;Mybtais的分页&quot;">​</a></h2><ul><li>内存分页：就是使用RowBounds对sql的执行结果ResultSet在内存中进行分页</li><li>物理分页：就是利用数据库支持的sql分页语法来实现。比如分页插件，它实现了Mybatis的插件接口，然后拦截查询请求，重写sql，在根据数据库方言，添加分页语句。</li></ul><h2 id="mybatis插件" tabindex="-1">Mybatis插件 <a class="header-anchor" href="#mybatis插件" aria-label="Permalink to &quot;Mybatis插件&quot;">​</a></h2><ul><li>可以拦截四种类型：ParameterHandler、ResultSetHandler、StatementHandler、Executor</li><li>Mybatis 使⽤ JDK 的动态代理，为需要拦截的接⼝⽣成代理对象以实现接⼝⽅法拦截功能，每当 执⾏这 4 种接⼝对象的⽅法时，就会进⼊拦截⽅法，具体就是 InvocationHandler 的 invoke() ⽅ 法，当然，只会拦截那些你指定需要拦截的⽅法</li><li>编写插件就是实现Interceptor接口的intercept()方法，再插件上添加注解@Intercepts(@Signature(拦截什么类的什么方法))</li></ul><h2 id="结果封装" tabindex="-1">结果封装 <a class="header-anchor" href="#结果封装" aria-label="Permalink to &quot;结果封装&quot;">​</a></h2><ul><li>使用resultMap标签，逐个定义列名和对象属性的映射关系</li><li>使用sql的别名，将查询结果的列名转换成跟结果对象的属性名相同的方式(多个单词数据库采用下划线分割，java对象属性使用小驼峰)</li><li>根据映射关系，mybtais通过反射创建对象，并对对象属性赋值返回</li></ul><h2 id="关联查询" tabindex="-1">关联查询 <a class="header-anchor" href="#关联查询" aria-label="Permalink to &quot;关联查询&quot;">​</a></h2><ul><li>两种方式 <ul><li>代码层面。先查主对象，然后再去根据管理id，查询子对象，使用代码层面关联。</li><li>数据库层面。基于数据库的关联查询，也就是join</li></ul></li><li>基于数据库join返回结果如何去重：基于主对象id进行去重，在resultMap中可以指定id（可以多个，联合主键）</li></ul><h2 id="延迟加载" tabindex="-1">延迟加载 <a class="header-anchor" href="#延迟加载" aria-label="Permalink to &quot;延迟加载&quot;">​</a></h2><ul><li>支持对association和collection的延迟加载，即利用代理，当需要通过主对象获取子对象的属性时，才会去数据库查询</li></ul><h2 id="执行器" tabindex="-1">执行器 <a class="header-anchor" href="#执行器" aria-label="Permalink to &quot;执行器&quot;">​</a></h2><p>sql执行器，负责调用StatementHandler完成SQL语句的生成和查询缓存维护</p><ul><li>SimpleExecutor：每次sql操作创建新的PrepareStatement，statement用完即关闭</li><li>ReuseExecutor：会复用PrepareStatement，对于相同的sql语句不会创建新的</li><li>BatchExecutor：批处理执行器，它会对更新操作进行批量处理，将所有的更新操作积攒到一定程度后再统一提交，这样可以减少网络传输次数，提高数据库操作的效率。</li></ul><p>默认是使用SimpleExecutor可以在DefaultSqlSessionFactory创建SqlSession时指定执行器</p><h2 id="typehandler" tabindex="-1">TypeHandler <a class="header-anchor" href="#typehandler" aria-label="Permalink to &quot;TypeHandler&quot;">​</a></h2><p>实现自定义映射。</p><ul><li>setParameter：javaType-jdbcType</li><li>getResult：jdbcType到javaType</li></ul><h2 id="xml与mybatis数据结构映射" tabindex="-1">xml与mybatis数据结构映射 <a class="header-anchor" href="#xml与mybatis数据结构映射" aria-label="Permalink to &quot;xml与mybatis数据结构映射&quot;">​</a></h2><p>整个xml对应Configuration对象</p><ul><li>resultMap标签被解析成ResultMap存储在Configuration的resultMaps属性</li><li>parameterMap标签被解析成ParameterMap存储在Configuration的parameterMaps属性</li><li>每个增删改查的标签会被解析成MappedStatement然后存储在Configuration的MappedStatements属性中</li><li>标签内的sql被解析成BoundSql，通过MappedStatement的SqlSource属性进行获取</li></ul><h1 id="微服务" tabindex="-1">微服务 <a class="header-anchor" href="#微服务" aria-label="Permalink to &quot;微服务&quot;">​</a></h1><h2 id="_1-微服务定义" tabindex="-1">1.微服务定义 <a class="header-anchor" href="#_1-微服务定义" aria-label="Permalink to &quot;1.微服务定义&quot;">​</a></h2><h3 id="什么是微服务" tabindex="-1">什么是微服务 <a class="header-anchor" href="#什么是微服务" aria-label="Permalink to &quot;什么是微服务&quot;">​</a></h3><ul><li>是一个分布式系统，按照业务划分成不同的服务单元，解决单体应用性能不足的问题</li><li>一个架构风格，一个大型应用由多个服务单元组成，系统中的服务单元可以独立部署，服务单元之间是松耦合的</li></ul><h2 id="_2-服务注册与发现" tabindex="-1">2.服务注册与发现 <a class="header-anchor" href="#_2-服务注册与发现" aria-label="Permalink to &quot;2.服务注册与发现&quot;">​</a></h2><p>服务注册就是维护一个服务的地址列表。帮助系统中多个服务单元可以相互找到彼此。</p><h3 id="eureka" tabindex="-1">Eureka <a class="header-anchor" href="#eureka" aria-label="Permalink to &quot;Eureka&quot;">​</a></h3><ul><li>Eureka Server提供注册服务，各个节点启动后向Server端注册</li><li>Eureka Client 集成在各个服务单元中，简化交互流程</li><li>Spring Cloud Netfix提供</li></ul><h3 id="zookeeper" tabindex="-1">Zookeeper <a class="header-anchor" href="#zookeeper" aria-label="Permalink to &quot;Zookeeper&quot;">​</a></h3><ul><li>Zookeeper 是一个集中的服务, 用于维护配置信息、命名、提供分布式同步和提供组服务。</li></ul><h3 id="_3-api网关" tabindex="-1">3.API网关 <a class="header-anchor" href="#_3-api网关" aria-label="Permalink to &quot;3.API网关&quot;">​</a></h3><h3 id="_4-配置中心" tabindex="-1">4.配置中心 <a class="header-anchor" href="#_4-配置中心" aria-label="Permalink to &quot;4.配置中心&quot;">​</a></h3><h3 id="_5-服务跟踪" tabindex="-1">5.服务跟踪 <a class="header-anchor" href="#_5-服务跟踪" aria-label="Permalink to &quot;5.服务跟踪&quot;">​</a></h3><h3 id="_6-服务熔断" tabindex="-1">6.服务熔断 <a class="header-anchor" href="#_6-服务熔断" aria-label="Permalink to &quot;6.服务熔断&quot;">​</a></h3><h3 id="_7-面试题" tabindex="-1">7.面试题 <a class="header-anchor" href="#_7-面试题" aria-label="Permalink to &quot;7.面试题&quot;">​</a></h3><h1 id="网络" tabindex="-1">网络 <a class="header-anchor" href="#网络" aria-label="Permalink to &quot;网络&quot;">​</a></h1><h3 id="_1-tcp-ip原理" tabindex="-1">1.TCP/IP原理 <a class="header-anchor" href="#_1-tcp-ip原理" aria-label="Permalink to &quot;1.TCP/IP原理&quot;">​</a></h3><h3 id="_2-tcp-三次握手和四次挥手" tabindex="-1">2.TCP 三次握手和四次挥手 <a class="header-anchor" href="#_2-tcp-三次握手和四次挥手" aria-label="Permalink to &quot;2.TCP 三次握手和四次挥手&quot;">​</a></h3><h3 id="_3-http原理" tabindex="-1">3.HTTP原理 <a class="header-anchor" href="#_3-http原理" aria-label="Permalink to &quot;3.HTTP原理&quot;">​</a></h3><h3 id="_4-cdn原理" tabindex="-1">4.CDN原理 <a class="header-anchor" href="#_4-cdn原理" aria-label="Permalink to &quot;4.CDN原理&quot;">​</a></h3><h1 id="负载均衡" tabindex="-1">负载均衡 <a class="header-anchor" href="#负载均衡" aria-label="Permalink to &quot;负载均衡&quot;">​</a></h1><h3 id="_1-负载均衡定义" tabindex="-1">1.负载均衡定义 <a class="header-anchor" href="#_1-负载均衡定义" aria-label="Permalink to &quot;1.负载均衡定义&quot;">​</a></h3><h3 id="_2-负载均衡模型" tabindex="-1">2.负载均衡模型 <a class="header-anchor" href="#_2-负载均衡模型" aria-label="Permalink to &quot;2.负载均衡模型&quot;">​</a></h3><h3 id="_3-负载均衡应用" tabindex="-1">3.负载均衡应用 <a class="header-anchor" href="#_3-负载均衡应用" aria-label="Permalink to &quot;3.负载均衡应用&quot;">​</a></h3><h1 id="分布式" tabindex="-1">分布式 <a class="header-anchor" href="#分布式" aria-label="Permalink to &quot;分布式&quot;">​</a></h1><h2 id="_1-cap" tabindex="-1">1.CAP <a class="header-anchor" href="#_1-cap" aria-label="Permalink to &quot;1.CAP&quot;">​</a></h2><ul><li>C：一致性</li><li>A：可用性</li><li>P：分区容忍性</li></ul><p>在分布式架构中三者不能同时做到，大部分场景是CP或者AP的场景</p><h2 id="_2-分布式事务" tabindex="-1">2.分布式事务 <a class="header-anchor" href="#_2-分布式事务" aria-label="Permalink to &quot;2.分布式事务&quot;">​</a></h2><h3 id="两阶段提交" tabindex="-1">两阶段提交 <a class="header-anchor" href="#两阶段提交" aria-label="Permalink to &quot;两阶段提交&quot;">​</a></h3><ul><li>准备阶段：应用程序通过事务协调者，向事务参与者发送提交请求，事务协调者等待事务参与者响应</li><li>提交阶段：事务协调者根据事务参与者的响应，决定是提交/中止事务</li></ul><p>TCC</p><ul><li>try：应用程序向事务参与者发送请求。事务参与者向事务协调者注册，然后处理本地事务，响应应用程序</li><li>confirm：应用程序获得所有正确的响应，向事务协调者发送确认请求，事务协调者通知所有事务参与者确认</li><li>cancel：应用程序获得异常的响应，向事务协调者发送取消请求，事务协调者通知已经注册的事务参与者取消</li></ul><h2 id="_3-分布式锁" tabindex="-1">3.分布式锁 <a class="header-anchor" href="#_3-分布式锁" aria-label="Permalink to &quot;3.分布式锁&quot;">​</a></h2><h3 id="redis实现的分布式锁" tabindex="-1">redis实现的分布式锁 <a class="header-anchor" href="#redis实现的分布式锁" aria-label="Permalink to &quot;redis实现的分布式锁&quot;">​</a></h3><h4 id="概念-1" tabindex="-1">概念 <a class="header-anchor" href="#概念-1" aria-label="Permalink to &quot;概念&quot;">​</a></h4><ul><li>基于redis的setnx指令，该指令当redis存在key则创建失败，不存在key则创建成功</li><li>加锁：setnx设置key，设置成功的获得锁</li><li>解锁：删除key，锁持有者可以删除</li></ul><h4 id="问题" tabindex="-1">问题 <a class="header-anchor" href="#问题" aria-label="Permalink to &quot;问题&quot;">​</a></h4><ul><li><p>死锁</p><blockquote><p>在setnx设置的key没有超时时间时</p><ul><li>正常死锁，临界资源，循环等待</li><li>锁持有者，崩溃，导致锁没有释放（key没有删除掉）</li></ul><p>setnx设置key时添加超时时间：可以解决死锁</p><ul><li>锁持有者的同步代码执行时间超过锁的有效时间，可能导致临界资源不是同步访问</li></ul><p>带来新问题：</p><p>锁持有者的同步代码执行超过锁的有效时间，在释放锁是可能释放错误的锁（释放了不是自己的锁）</p><ul><li>通过给value设置一个随机数来保证释放的锁是自己的锁。</li></ul></blockquote></li><li><p>不支持阻塞</p><blockquote><p>自行实现自旋：消耗cpu性能</p></blockquote></li></ul><h3 id="zookeeper实现的分布式锁" tabindex="-1">zookeeper实现的分布式锁 <a class="header-anchor" href="#zookeeper实现的分布式锁" aria-label="Permalink to &quot;zookeeper实现的分布式锁&quot;">​</a></h3><h4 id="基于单个临时节点" tabindex="-1">基于单个临时节点 <a class="header-anchor" href="#基于单个临时节点" aria-label="Permalink to &quot;基于单个临时节点&quot;">​</a></h4><ul><li>利用zk的节点只能被一个客户端创建成功的特性</li><li>多个客户端，创建临时节点成功的客户端，获得锁</li><li>没有获得锁的客户端，在临时节点上监听，当临时节点移除时，重新进行竞争</li></ul><h4 id="基于临时顺序节点" tabindex="-1">基于临时顺序节点 <a class="header-anchor" href="#基于临时顺序节点" aria-label="Permalink to &quot;基于临时顺序节点&quot;">​</a></h4><ul><li>利用zk临时顺序节点的特点</li><li>多个客户端创建的节点是有顺序的，序号最小的获得锁</li><li>每个客户端去监听比它序号小的前的一个节点</li><li>释放锁时，就是删除自己创建的节点，后一个节点监听到节点删除，就会判断自己是不是最小的，若是最小的即可获取锁</li></ul><h3 id="其他分布式锁" tabindex="-1">其他分布式锁 <a class="header-anchor" href="#其他分布式锁" aria-label="Permalink to &quot;其他分布式锁&quot;">​</a></h3><h4 id="基于mysql实现分布式锁" tabindex="-1">基于Mysql实现分布式锁 <a class="header-anchor" href="#基于mysql实现分布式锁" aria-label="Permalink to &quot;基于Mysql实现分布式锁&quot;">​</a></h4><ul><li>利用Mysql的主键唯一性</li></ul><h2 id="_4-分布式一致性算法" tabindex="-1">4.分布式一致性算法 <a class="header-anchor" href="#_4-分布式一致性算法" aria-label="Permalink to &quot;4.分布式一致性算法&quot;">​</a></h2><h1 id="dubbo" tabindex="-1">Dubbo <a class="header-anchor" href="#dubbo" aria-label="Permalink to &quot;Dubbo&quot;">​</a></h1><h1 id="zookeeper-1" tabindex="-1">Zookeeper <a class="header-anchor" href="#zookeeper-1" aria-label="Permalink to &quot;Zookeeper&quot;">​</a></h1><h3 id="_1-概念" tabindex="-1">1.概念 <a class="header-anchor" href="#_1-概念" aria-label="Permalink to &quot;1.概念&quot;">​</a></h3><h3 id="_2-角色" tabindex="-1">2.角色 <a class="header-anchor" href="#_2-角色" aria-label="Permalink to &quot;2.角色&quot;">​</a></h3><h3 id="_3-工作原理" tabindex="-1">3.工作原理 <a class="header-anchor" href="#_3-工作原理" aria-label="Permalink to &quot;3.工作原理&quot;">​</a></h3><h3 id="_4-4种形式的目录节点" tabindex="-1">4. 4种形式的目录节点 <a class="header-anchor" href="#_4-4种形式的目录节点" aria-label="Permalink to &quot;4. 4种形式的目录节点&quot;">​</a></h3><h1 id="mq" tabindex="-1">MQ <a class="header-anchor" href="#mq" aria-label="Permalink to &quot;MQ&quot;">​</a></h1><h3 id="_1-各种mq比较" tabindex="-1">1.各种MQ比较 <a class="header-anchor" href="#_1-各种mq比较" aria-label="Permalink to &quot;1.各种MQ比较&quot;">​</a></h3><h3 id="_2-rabbitmq" tabindex="-1">2.RabbitMQ <a class="header-anchor" href="#_2-rabbitmq" aria-label="Permalink to &quot;2.RabbitMQ&quot;">​</a></h3><h3 id="_3-kafka" tabindex="-1">3.Kafka <a class="header-anchor" href="#_3-kafka" aria-label="Permalink to &quot;3.Kafka&quot;">​</a></h3><h1 id="数据库" tabindex="-1">数据库 <a class="header-anchor" href="#数据库" aria-label="Permalink to &quot;数据库&quot;">​</a></h1><h3 id="_1-数据库基本概念" tabindex="-1">1.数据库基本概念 <a class="header-anchor" href="#_1-数据库基本概念" aria-label="Permalink to &quot;1.数据库基本概念&quot;">​</a></h3><h3 id="_2-mysql" tabindex="-1">2.MySQL <a class="header-anchor" href="#_2-mysql" aria-label="Permalink to &quot;2.MySQL&quot;">​</a></h3><ul><li>分库分表</li><li>读写分离</li></ul><h3 id="_3-redis" tabindex="-1">3.Redis <a class="header-anchor" href="#_3-redis" aria-label="Permalink to &quot;3.Redis&quot;">​</a></h3><h3 id="_4-es" tabindex="-1">4.ES <a class="header-anchor" href="#_4-es" aria-label="Permalink to &quot;4.ES&quot;">​</a></h3><h1 id="" tabindex="-1"><a class="header-anchor" href="#" aria-label="Permalink to &quot;&quot;">​</a></h1><h1 id="其他" tabindex="-1">其他 <a class="header-anchor" href="#其他" aria-label="Permalink to &quot;其他&quot;">​</a></h1><h3 id="_1-saas-pass-iaas" tabindex="-1">1.SAAS PASS IAAS <a class="header-anchor" href="#_1-saas-pass-iaas" aria-label="Permalink to &quot;1.SAAS PASS IAAS&quot;">​</a></h3><h3 id="_2-docker" tabindex="-1">2.Docker <a class="header-anchor" href="#_2-docker" aria-label="Permalink to &quot;2.Docker&quot;">​</a></h3><h3 id="_3-k8s" tabindex="-1">3.K8S <a class="header-anchor" href="#_3-k8s" aria-label="Permalink to &quot;3.K8S&quot;">​</a></h3></div></div></main><footer class="VPDocFooter" data-v-39a288b8 data-v-09de1c0f><!--[--><!--]--><!----><!----></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
    <script>window.__VP_HASH_MAP__=JSON.parse("{\"cs-base_network_network1.md\":\"BnWFWINK\",\"cs-base_dsa_tree_二叉树.md\":\"DQOrmvZq\",\"cs-base_dsa_linked-list_链表.md\":\"BPvkxAmv\",\"index.md\":\"B5b0LM_f\",\"cs-base_dsa_tree_树.md\":\"BybQp763\",\"test.md\":\"DXBSkH1F\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"zh-cn\",\"dir\":\"ltr\",\"title\":\"爱做梦的锤子\",\"description\":\"爱做梦的锤子\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"logo\":{\"src\":\"/logo.png\",\"alt\":\"te-amo\"},\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"计算机基础\",\"items\":[{\"text\":\"数据结构与算法\",\"link\":\"/test\"}]}],\"sidebar\":{\"/cs-base/dsa\":[{\"text\":\"链表\",\"items\":[{\"text\":\"链表\",\"link\":\"/cs-base/dsa/linked-list/链表\"}]},{\"text\":\"树\",\"items\":[{\"text\":\"树\",\"link\":\"/cs-base/dsa/tree/树\"},{\"text\":\"二叉树\",\"link\":\"/cs-base/dsa/tree/二叉树\"}]}],\"/cs-base/network\":[{\"text\":\"网络\",\"items\":[{\"text\":\"网络模型\",\"link\":\"/cs-base/dsa/linked-list/链表\"},{\"text\":\"HTTP\",\"link\":\"/cs-base/dsa/linked-list/链表\"}]}]},\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/vuejs/vitepress\"}]},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":false}");</script>
    
  </body>
</html>